#include "core/frame.hpp"
#include "yocto/z++/gzstream.hpp"

#include "yocto/string/tokenizer.hpp"
#include "yocto/sequence/vector.hpp"
#include "yocto/memory/pooled.hpp"
#include "yocto/exception.hpp"
#include "yocto/auto-ptr.hpp"

namespace FastMD 
{
	
#define FMT "%.5e"
	void Frame:: save( const string &filename, const Real t, bool append ) const
	{
		ios::ogzstream fp( filename, 9, append );
		fp("N %u\n", unsigned(particles));
		fp("L " FMT " " FMT " " FMT "\n", box.x, box.y, box.z );
		fp("t " FMT "\n",t);
		fp("V " FMT "\n",V);
		fp("K " FMT "\n",K);
		const Vector &pos = vector_[ POSITIONS  ];
		const Vector &vel = vector_[ VELOCITIES ];
		for( size_t i=0; i < particles; ++i )
		{
			const V3D &r = pos[i];
			const V3D &v = vel[i];
			// mass inv_mass>0 
			fp(FMT " %d", v.data, (r.data > 0 ? 1 : 0) );
			fp(" " FMT " " FMT " " FMT, r.x, r.y, r.z);
			fp(" " FMT " " FMT " " FMT, v.x, v.y, v.z);
			fp("\n");
		}
	}
	
	static inline void __split(vector<string>    &words, 
							   string            &line, 
							   const char        *first,
							   const size_t       num_args,
							   vector<Real>      &args)
	{
		assert( first );
		words.free();
		tokenizer::split( words, line, character<char>::is_space );
		if( words.size() <= 0 )
			throw exception("Empty line, expecting '%s ...'", first );
		if( words[1] != first )
			throw exception("Expecting '%s', got '%s'", first, words[1].c_str() );
		if( words.size() <= num_args )
			throw exception("Missing %u value(s) after '%s'", unsigned((num_args+1) - words.size()), first);
		args.free();
		for( size_t i=1; i <= num_args; ++i )
		{
			const Real value = Real( strtod( words[i+1].c_str(), NULL) );
			args.push_back( value );
		}
		assert( args.size() >= num_args );
		line.clear();
	}
	
	
	Frame * Frame:: load( ios::istream &fp, Real &t, const size_t num_vectors)
	{
		string         line;
		vector<string> words;
		vector<Real>   args;
		
		//======================================================================
		// N
		//======================================================================
		if( fp.read_line( line ) < 0 )
		{
			return NULL; //-- no new frame...
		}
		__split(words,line,"N",1,args);
		const size_t N = size_t(args[1]);
		if( N <= 0 ) throw exception( "invalid #particles" );
		std::cerr << "N=" << N << std::endl;
		
		//======================================================================
		// L
		//======================================================================
		if( fp.read_line( line ) < 0 )
			throw exception("Missing L");
		__split(words,line,"L",3,args);
		const V3D L( args[1], args[2], args[3] );
		if( L.x <= 0 || L.y <= 0 || L.z <= 0 ) throw exception("Invalid box(%g,%g,%g)", L.x, L.y, L.z );
		std::cerr << "L=" << L << std::endl;
		
		//======================================================================
		// t
		//======================================================================
		if( fp.read_line( line ) < 0 )
			throw exception("Missing t");
		__split( words, line, "t", 1, args );
		t = args[1];
		std::cerr << "t=" << t << std::endl;
		
		//======================================================================
		// V
		//======================================================================
		if( fp.read_line( line ) < 0 )
			throw exception("Missing V");
		__split( words, line, "V", 1, args );
		const Real V = args[1];
		std::cerr << "V=" << V << std::endl;
		
		//======================================================================
		// K
		//======================================================================
		if( fp.read_line( line ) < 0 )
			throw exception("Missing K");
		__split( words, line, "K", 1, args );
		const Real K = args[1];
		std::cerr << "K=" << K << std::endl;
		
		
		//======================================================================
		// Allocating resources
		//======================================================================
		auto_ptr<Frame> frame( new Frame( num_vectors, N ) );
		Vector &pos = (*frame)[ POSITIONS  ];
		Vector &vel = (*frame)[ VELOCITIES ];
		frame->set_box( L );
		//======================================================================
		// Loading frame
		//======================================================================
		for( size_t i=0; i < N; ++i )
		{
			V3D &r = pos[i];
			V3D &v = vel[i];
			
			//------------------------------------------------------------------
			// get data
			//------------------------------------------------------------------
			const unsigned pidx = unsigned(i+1);
			if( fp.read_line(line) < 0 )
				throw exception("Missing particle #%u", pidx);
			
			//------------------------------------------------------------------
			// split input
			//------------------------------------------------------------------
			words.free();
			tokenizer::split( words, line, character<char>::is_space );
			line.clear();
			size_t j=0;
			if( words.size() < 8 ) throw exception("Missing info for particle #%u", pidx);
			
			//--- mass
			const Real mass = strtod( words[++j].c_str(), NULL);
			v.data = mass;
			
			//--- imass
			const long compute_imass = strtol(words[++j].c_str(), NULL, 10);
			if( compute_imass ) {
				r.data = 1/mass;
			}
			else {
				r.data = 0;
			}
			
			//--- r
			r.x = strtod( words[++j].c_str(), NULL);
			r.y = strtod( words[++j].c_str(), NULL);
			r.z = strtod( words[++j].c_str(), NULL);
			frame->pbc_r(r,frame->box,frame->ibox);
			
			//-- v
			v.x = strtod( words[++j].c_str(), NULL);
			v.y = strtod( words[++j].c_str(), NULL);
			v.z = strtod( words[++j].c_str(), NULL);
			
			
		}
		
		return frame.yield();
	}
	
	
}

